#include "QD3DUtilsCvExt.h"


QD3DUtilsCvExt::QD3DUtilsCvExt() : QD3DUtils ()
{

}

QD3DUtilsCvExt::QD3DUtilsCvExt (QD3DContext *pContext) : QD3DUtils (pContext)
{

}

QD3DUtilsCvExt::~QD3DUtilsCvExt ()
{

}

ID3D11Texture2D * QD3DUtilsCvExt::createTexture2DFromCvImage (IplImage *pImage,
		UINT bindFlag)
{
	DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
	uchar *pImageData = NULL;
	UINT lineBytes = 0;
	bool isNew = _getDxTexDataFromCvImage (pImage, &pImageData,
			&lineBytes, &format);
	CD3D11_TEXTURE2D_DESC desc (format, pImage->width, pImage->height, 1, 1,
			bindFlag);
	D3D11_SUBRESOURCE_DATA subsource = { pImageData, lineBytes, 0 };
	QVector< D3D11_SUBRESOURCE_DATA > subresources (1);
	subresources[0] = subsource;
	ID3D11Texture2D *pTexObj = createTexture2D (desc, subresources);
	if (isNew)
		delete [] pImageData;
	return pTexObj;
}

bool QD3DUtilsCvExt::_getDxTexDataFromCvImage (IplImage *pImage,
		uchar **ppOutTexData, UINT *pOutLineBytes, DXGI_FORMAT *pOutFormat)
{
	DXGI_FORMAT &format = *pOutFormat;
	uchar *&pImageData = *ppOutTexData;
	UINT &lineBytes = *pOutLineBytes;
	bool isNew = false;

	if (pImage->depth == 8 && pImage->nChannels == 3) {
		// RGB Image
		pImageData = new uchar[pImage->width * pImage->height * 4];
		isNew = true;
		for (int v = 0; v < pImage->height; ++v) {
			for (int u = 0; u < pImage->width; ++u) {
				pImageData[(v * pImage->width + u) * 4 + 0] = CV_IMAGE_ELEM (pImage, uchar, v, u * 3 + 2);
				pImageData[(v * pImage->width + u) * 4 + 1] = CV_IMAGE_ELEM (pImage, uchar, v, u * 3 + 1);
				pImageData[(v * pImage->width + u) * 4 + 2] = CV_IMAGE_ELEM (pImage, uchar, v, u * 3 + 0);
				pImageData[(v * pImage->width + u) * 4 + 3] = 255;
			}
		}
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		lineBytes = pImage->width * 4;
	} else if (pImage->depth == 8 && pImage->nChannels == 4) {
		// RGB Image
		pImageData = new uchar[pImage->width * pImage->height * 4];
		isNew = true;
		for (int v = 0; v < pImage->height; ++v) {
			for (int u = 0; u < pImage->width; ++u) {
				pImageData[(v * pImage->width + u) * 4 + 0] = CV_IMAGE_ELEM (pImage, uchar, v, u * 4 + 3);
				pImageData[(v * pImage->width + u) * 4 + 1] = CV_IMAGE_ELEM (pImage, uchar, v, u * 4 + 2);
				pImageData[(v * pImage->width + u) * 4 + 2] = CV_IMAGE_ELEM (pImage, uchar, v, u * 4 + 1);
				pImageData[(v * pImage->width + u) * 4 + 3] = CV_IMAGE_ELEM (pImage, uchar, v, u * 4 + 0);
			}
		}
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		lineBytes = pImage->width * 4;
	} else if (pImage->depth == 8 && pImage->nChannels == 1) {
		pImageData = (uchar *)pImage->imageData;
		format = DXGI_FORMAT_R8_UINT;
		lineBytes = pImage->widthStep;
	} else if (pImage->depth == 16 && pImage->nChannels == 1) {
		pImageData = (uchar *)pImage->imageData;
		format = DXGI_FORMAT_R16_UINT;
		lineBytes = pImage->widthStep;
	} else {
		throw std::exception ("QD3DUtilsCvExt::_getDxTexDataFromCvImage(): "
				"Format not support.");
	}
	return isNew;
}

void QD3DUtilsCvExt::updateTexture2DFromCvImage (ID3D11Texture2D *pDstDxTex,
		IplImage *pImage)
{
	DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
	uchar *pImageData = NULL;
	UINT lineBytes = 0;
	bool isNew = _getDxTexDataFromCvImage (pImage, &pImageData,
			&lineBytes, &format);
	updateSubresource (pDstDxTex, 0, pImageData, NULL, lineBytes);
	if (isNew)
		delete [] pImageData;
}

void QD3DUtilsCvExt::downloadCvImageFromTexture2D (
		ID3D11Texture2D * p_dxTex, IplImage * p_image)
{
	D3D11_TEXTURE2D_DESC texDesc;
	p_dxTex->GetDesc (&texDesc);
	D3D11_MAPPED_SUBRESOURCE subResource;
	map (D3D11_MAP_READ, p_dxTex, &subResource);

	switch (texDesc.Format) {
	case DXGI_FORMAT_R8G8B8A8_UNORM:
		if (p_image->nChannels == 4 && p_image->depth == 8) {
			for (int v = 0; v < p_image->height; ++v){
				for (int u = 0; u < p_image->width; ++v) {
					CV_IMAGE_ELEM (p_image, uchar, v, u * 4) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4 + 3];
					CV_IMAGE_ELEM (p_image, uchar, v, u * 4 + 1) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4 + 2];
					CV_IMAGE_ELEM (p_image, uchar, v, u * 4 + 2) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4 + 1];
					CV_IMAGE_ELEM (p_image, uchar, v, u * 4 + 3) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4];
				}
			}
		} else if (p_image->nChannels == 3 && p_image->depth == 8) {
			for (int v = 0; v < p_image->height; ++v){
				for (int u = 0; u < p_image->width; ++v) {
					CV_IMAGE_ELEM (p_image, uchar, v, u * 3) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4 + 2];
					CV_IMAGE_ELEM (p_image, uchar, v, u * 3 + 1) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4 + 1];
					CV_IMAGE_ELEM (p_image, uchar, v, u * 3 + 2) = ((char*)(subResource.pData))[v * subResource.RowPitch + u * 4];
				}
			}
		} else
			throw std::exception ("QD3DUtilsCvExt::downloadCvImageFromTexture2D(): "
					"Format mismatch");
		break;
	case DXGI_FORMAT_R8_UINT:
		if (p_image->nChannels != 1 || p_image->depth != 8)
			throw std::exception ("QD3DUtilsCvExt::downloadCvImageFromTexture2D(): "
					"Format mismatch");
		for (int v = 0; v < p_image->height; ++v)
			memcpy (p_image->imageData + v * p_image->widthStep,
					(char*)subResource.pData + v * subResource.RowPitch,
					p_image->widthStep);
		break;
	case DXGI_FORMAT_R16_UINT:
		if (p_image->nChannels != 1 || p_image->depth != 16)
			throw std::exception ("QD3DUtilsCvExt::downloadCvImageFromTexture2D(): "
					"Format mismatch");
		for (int v = 0; v < p_image->height; ++v)
			memcpy (p_image->imageData + v * p_image->widthStep,
					(char*)subResource.pData + v * subResource.RowPitch,
					p_image->widthStep);
		break;
	}
	unmap (p_dxTex);
}
